/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.mechanatrix;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.CameraControl;
import com.jme3.scene.shape.Box;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import com.jme3.util.SkyFactory;

/**
 *
 * @author dnigels
 */
public class Test1stPerson extends SimpleApplication implements ActionListener {
	
	private BulletAppState bulletAppState;
	private BetterCharacterControl physicsCharacter;
	private Node characterNode;
	private CameraNode camNode;
    boolean rotate = false;
    private Vector3f walkDirection = new Vector3f(0, 0, 0);
    private Vector3f viewDirection = new Vector3f(0, 0, 1);
    boolean leftStrafe = false, rightStrafe = false, forward = false, backward = false,
            leftRotate = false, rightRotate = false;

	public static void main(String[] args) {
		Test1stPerson app = new Test1stPerson();
		app.start();
	}
	
	private PhysicsSpace getPhysicsSpace() {
		return bulletAppState.getPhysicsSpace();
	}

	
	private void cloneAndPlace(Spatial spatial, float x, float y, float z) {
		Spatial clone = spatial.clone();
		clone.move(x, y, z);
		rootNode.attachChild(clone);
        clone.addControl(new RigidBodyControl(0));
        bulletAppState.getPhysicsSpace().addAll(clone);
	}

	@Override
	public void simpleInitApp() {
		
		setupKeys();
		
		bulletAppState = new BulletAppState();
		stateManager.attach(bulletAppState);
		
		initTerrain();
		
		initLighting();
		
        // Create a node for the character model
        characterNode = new Node("character node");
        characterNode.setLocalTranslation(new Vector3f(4, 5, 2));

        // Add a character control to the node so we can add other things and
        // control the model rotation
        physicsCharacter = new BetterCharacterControl(0.3f, 2.5f, 8f);
        characterNode.addControl(physicsCharacter);
        getPhysicsSpace().add(physicsCharacter);

        // Load model, attach to character node
        Spatial model = assetManager.loadModel("Models/saguaro/saguaro.obj");
        model.setLocalScale(0.01f);;
        characterNode.attachChild(model);

        // Add character node to the rootNode
        rootNode.attachChild(characterNode);

        // Set forward camera node that follows the character, only used when
        // view is "locked"
        camNode = new CameraNode("CamNode", cam);
        camNode.setControlDir(CameraControl.ControlDirection.SpatialToCamera);
        camNode.setLocalTranslation(new Vector3f(0, 2, -6));
        Quaternion quat = new Quaternion();
        // These coordinates are local, the camNode is attached to the character node!
        quat.lookAt(Vector3f.UNIT_Z, Vector3f.UNIT_Y);
        camNode.setLocalRotation(quat);
        characterNode.attachChild(camNode);
        // Disable by default, can be enabled via keyboard shortcut
        camNode.setEnabled(false);
	}

	@Override
    public void onAction(String binding, boolean value, float tpf) {
        if (binding.equals("Strafe Left")) {
            if (value) {
                leftStrafe = true;
            } else {
                leftStrafe = false;
            }
        } else if (binding.equals("Strafe Right")) {
            if (value) {
                rightStrafe = true;
            } else {
                rightStrafe = false;
            }
        } else if (binding.equals("Rotate Left")) {
            if (value) {
                leftRotate = true;
            } else {
                leftRotate = false;
            }
        } else if (binding.equals("Rotate Right")) {
            if (value) {
                rightRotate = true;
            } else {
                rightRotate = false;
            }
        } else if (binding.equals("Walk Forward")) {
            if (value) {
                forward = true;
            } else {
                forward = false;
            }
        } else if (binding.equals("Walk Backward")) {
            if (value) {
                backward = true;
            } else {
                backward = false;
            }
        } else if (binding.equals("Jump")) {
            physicsCharacter.jump();
        } else if (binding.equals("Duck")) {
            if (value) {
                physicsCharacter.setDucked(true);
            } else {
                physicsCharacter.setDucked(false);
            }
        } else if (binding.equals("Lock View")) {
            if (value && lockView) {
                lockView = false;
            } else if (value && !lockView) {
                lockView = true;
            }
            flyCam.setEnabled(!lockView);
            camNode.setEnabled(lockView);
        }
    }
    private boolean lockView = false;

    @Override
    public void simpleUpdate(float tpf) {

        // Get current forward and left vectors of model by using its rotation
        // to rotate the unit vectors
        Vector3f modelForwardDir = characterNode.getWorldRotation().mult(Vector3f.UNIT_Z);
        Vector3f modelLeftDir = characterNode.getWorldRotation().mult(Vector3f.UNIT_X);

        // WalkDirection is global!
        // You *can* make your character fly with this.
        walkDirection.set(0, 0, 0);
        if (leftStrafe) {
            walkDirection.addLocal(modelLeftDir.mult(3));
        } else if (rightStrafe) {
            walkDirection.addLocal(modelLeftDir.negate().multLocal(3));
        }
        if (forward) {
            walkDirection.addLocal(modelForwardDir.mult(3));
        } else if (backward) {
            walkDirection.addLocal(modelForwardDir.negate().multLocal(3));
        }
        physicsCharacter.setWalkDirection(walkDirection);

        // ViewDirection is local to characters physics system!
        // The final world rotation depends on the gravity and on the state of
        // setApplyPhysicsLocal()
        if (leftRotate) {
            Quaternion rotateL = new Quaternion().fromAngleAxis(FastMath.PI * tpf, Vector3f.UNIT_Y);
            rotateL.multLocal(viewDirection);
        } else if (rightRotate) {
            Quaternion rotateR = new Quaternion().fromAngleAxis(-FastMath.PI * tpf, Vector3f.UNIT_Y);
            rotateR.multLocal(viewDirection);
        }
        physicsCharacter.setViewDirection(viewDirection);
        fpsText.setText("Touch da ground = " + physicsCharacter.isOnGround());
        if (!lockView) {
            cam.lookAt(characterNode.getWorldTranslation().add(new Vector3f(0, 2, 0)), Vector3f.UNIT_Y);
        }
    }
	
	private void initLighting() {

		AmbientLight light = new AmbientLight();
		light.setColor(ColorRGBA.LightGray);
		rootNode.addLight(light);

		DirectionalLight sun = new DirectionalLight();
		sun.setDirection(new Vector3f(-FastMath.QUARTER_PI, -FastMath.QUARTER_PI, 1.0f));
		rootNode.addLight(sun);

		DirectionalLightShadowRenderer dlsr = new DirectionalLightShadowRenderer(assetManager, 1024, 3);
		dlsr.setLight(sun);
		viewPort.addProcessor(dlsr);
		rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Hot Summer Day.dds", false));

	}

	private void initTerrain() {
		Box floorBox = new Box(512, 0.25f, 512);
		floorBox.scaleTextureCoordinates(new Vector2f(64, 64));
		Geometry floorGeometry = new Geometry("Floor", floorBox);
		floorGeometry.setShadowMode(RenderQueue.ShadowMode.Receive);
		floorGeometry.setMaterial(assetManager.loadMaterial("MatDefs/ground_01.j3m"));
		floorGeometry.addControl(new RigidBodyControl(0));
		rootNode.attachChild(floorGeometry);
		getPhysicsSpace().add(floorGeometry);
		
		Spatial yucca = assetManager.loadModel("Models/yucca/yucca.j3o");
		yucca.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
		yucca.scale(FastMath.pow(2f, -3f));
		cloneAndPlace(yucca, 120f, 0.25f, 15f);
		cloneAndPlace(yucca, 160f, 0.25f, 150f);
		cloneAndPlace(yucca, 150f, 0.25f, 50f);
		cloneAndPlace(yucca, 180f, 0.25f, -30f);
		cloneAndPlace(yucca, 80f, 0.25f, 200f);
	}

	private void setupKeys() {
        inputManager.addMapping("Strafe Left",
                new KeyTrigger(KeyInput.KEY_U),
                new KeyTrigger(KeyInput.KEY_Z));
        inputManager.addMapping("Strafe Right",
                new KeyTrigger(KeyInput.KEY_O),
                new KeyTrigger(KeyInput.KEY_X));
        inputManager.addMapping("Rotate Left",
                new KeyTrigger(KeyInput.KEY_J),
                new KeyTrigger(KeyInput.KEY_LEFT));
        inputManager.addMapping("Rotate Right",
                new KeyTrigger(KeyInput.KEY_L),
                new KeyTrigger(KeyInput.KEY_RIGHT));
        inputManager.addMapping("Walk Forward",
                new KeyTrigger(KeyInput.KEY_I),
                new KeyTrigger(KeyInput.KEY_UP));
        inputManager.addMapping("Walk Backward",
                new KeyTrigger(KeyInput.KEY_K),
                new KeyTrigger(KeyInput.KEY_DOWN));
        inputManager.addMapping("Jump",
                new KeyTrigger(KeyInput.KEY_F),
                new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addMapping("Duck",
                new KeyTrigger(KeyInput.KEY_G),
                new KeyTrigger(KeyInput.KEY_LSHIFT),
                new KeyTrigger(KeyInput.KEY_RSHIFT));
        inputManager.addMapping("Lock View",
                new KeyTrigger(KeyInput.KEY_RETURN));
        inputManager.addListener(this, "Strafe Left", "Strafe Right");
        inputManager.addListener(this, "Rotate Left", "Rotate Right");
        inputManager.addListener(this, "Walk Forward", "Walk Backward");
        inputManager.addListener(this, "Jump", "Duck", "Lock View");
	}
	
}
